/*
 * @Author:黄威
 * @Date:2021-10-27 09:42:10
 * @LastEditors: 黄威
 * @LastEditTime: 2022-02-15 16:48:38
 * @Description:后台分析服务
 */
import type { AnalysisOption } from 'types'

import request, { getToken } from '@/utils/request'
import WKT from 'terraformer-wkt-parser'

declare interface APIConfig {
    defalutData?: any
    request?: (...data: any[]) => any
    response?: (data: any) => any
}

// 根据配置构造一个请求器
function getAnalysisService<T extends { [key: string]: APIConfig }>(
    nameSpace: string,
    apiConfig: T,
    requestMethod: (url: string, data: any) => Promise<any>
): { [key in keyof T]: (...rest: any[]) => Promise<any> } {
    const ret: any = {}

    const keys = Object.keys(apiConfig)
    for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        const config = apiConfig[key]

        // 构造请求方法
        ret[key] = async function (...rest: any[]) {
            let data = {}

            // 优先处理
            if (config.request) {
                data = await config.request.call(ret, ...rest)
            }
            // 附加默认值
            if (config.defalutData) {
                data = Object.assign({}, config.defalutData, data)
            }

            // 后处理
            const res = await requestMethod(nameSpace + '/' + key, data)
            if (config.response) {
                return config.response(res)
            }
            return res
        }
    }

    return ret
}

// 导航用的配置
const navConfig: APIConfig = {
    request(
        start: [number, number],
        end: [number, number],
        through: [number, number][] = [],
        wkt?: string
    ) {
        const data: any = {
            point2Ds: [
                { x: start[0], y: start[1] },
                ...through.map(ll => ({ x: ll[0], y: ll[1] })),
                { x: end[0], y: end[1] },
            ],
        }
        // 兼容 最短表面距离路径分析
        if (wkt !== undefined) {
            data.wkt = wkt
        }
        return data
    },
    response(res) {
        const geolines = []

        let i = 1
        while (res['geoline_' + i]) {
            geolines.push((WKT.parse(res['geoline_' + i]) as any).coordinates)
            i++
        }

        return {
            cost_time: res.cost_time,
            geolines: geolines,
        }
    },
}
// 导航用的配置
const navConfig2: APIConfig = {
    request(
        start: [number, number],
        end: [number, number],
        through: [number, number][] = [],
        custom?: AnalysisOption
    ) {
        const data: any = {
            point2DS: [
                { x: start[0], y: start[1] },
                ...through.map(ll => ({ x: ll[0], y: ll[1] })),
                { x: end[0], y: end[1] },
            ],
            ...custom,
        }
        return data
    },
    response(navData) {
        const {
            pathGuideItemTurnTypes,
            pathGuides,
            pathGuideItemWeights,
            geoLineMs,
        } = navData

        // 方向信息
        const turnTypes = pathGuideItemTurnTypes
            .slice(1, -1)
            .split(', ')
            .map((item: string) => item.split('=')[1])

        // 路线信息
        pathGuides.length = Object.keys(pathGuides).length
        const guides = Array.from(pathGuides)

        // 权重信息（可以由此得到线路信息）
        pathGuideItemWeights.length = Object.keys(pathGuideItemWeights).length
        const weights = Array.from(pathGuideItemWeights)

        // 有效的线路信息
        const linesMessage = []
        let preWeight = 0

        for (let i = 0; i < weights.length; i++) {
            const weight = weights[i] as number

            if (weight > 0) {
                preWeight += weight

                linesMessage.push({
                    // 线段名称和转向
                    guide: guides[i],
                    turnType: turnTypes[i - 1],
                    // 权重信息
                    weight: weight,
                    totalWeight: preWeight,
                })
            }
        }

        // 返回线段信息 和 节点信息
        return [
            {
                // 总距离
                distance: preWeight,
                // 线段信息
                linesMessage,
                // 点信息
                geoLineMs,
            },
        ]
    },
}
// 两点配置
const twoPointConfig: APIConfig = {
    request(p1, p2) {
        return {
            fromPoint: {
                x: p1[0],
                y: p1[1],
                z: p1[2] || 0,
            },
            toPoint: {
                x: p2[0],
                y: p2[1],
                z: p2[2] || 0,
            },
        }
    },
}
// 转为wkt格式多边形
const WKTPolygon = function (ps: any) {
    if (ps.length < 3) throw '至少需要三个坐标点！'

    let polygonStr = ps.map((p: any) => `${p[0]} ${p[1]}`).join(',')
    polygonStr += ',' + ps[0][0] + ' ' + ps[0][1]

    return {
        wkt: `Polygon((${polygonStr}))`,
    }
}
// 转为wkt格式线段
const WKTLineString = function (ps: any) {
    if (ps.length < 2) throw '至少需要二个坐标点！'

    const lineStringStr = ps.map((p: any) => `${p[0]} ${p[1]}`).join(',')

    return {
        wkt: `LineString(${lineStringStr})`,
    }
}
// 选择结果字段
const pickResponse = function (key: string) {
    return function (data: any) {
        return data[key]
    }
}

export default function (options: AnalysisOption) {
    const RequestMethod = async function (url: string, data: any) {
        const res = await request({
            method: 'POST',
            baseURL: options.url,
            url: '/geo/' + url,
            data: {
                datasource: options.datasource,
                dataset: options.dataset,
                fileName: options.fileName,
                ...data,
            },
        })
        // 状态判断
        if (res.data.code === 200) {
            return res.data.data
        }
        throw res.data.msg
    }

    const caculate = getAnalysisService(
        'caculate',
        {
            // 区域高程统计
            altitudeStatistics: {},
            // 高程分析
            calculateAltitude: {
                request(p) {
                    return { pointX: p[0], pointY: p[1] }
                },
            },
            // 地表面积
            calculateArea: {
                request: WKTPolygon,
                response: pickResponse('area'),
            },
            // 地表距离
            calculateDistance: {
                request: WKTLineString,
                response: pickResponse('distance'),
            },
            // 剖面分析
            calculateProfile: {
                request: WKTLineString,
            },
            // 坡度坡向分析
            calculateSlopeOrAspect: {
                defalutData: {
                    func: 'Slope',
                    azimuth: 315,
                    zfactor: 0.00001,
                },
                request(data) {
                    const { points, width = 300, height = 300, ...rest } = data
                    return {
                        geoarea: WKTPolygon(points).wkt,
                        imgWidth: width * 2,
                        imgHeigth: height * 2,
                        ...rest,
                    }
                },
                response(res) {
                    const baseUrl =
                        options.url +
                        '/geo/caculate/getCalculateSlopeOrAspectImg'

                    res.image =
                        baseUrl +
                        `?hdfspath=${res.hdfsPath}&fileName=${
                            res.fileName
                        }&token=${getToken()}`
                    return res
                },
            },
            // 填挖方分析
            cutFill: {},
            // 空间查询
            findByGeometry: {},
            // 淹没分析
            flood: {},
            // 点坡向
            getPointAspect: {},
            // 点坡度
            getPointSlope: {},
            // 通视分析(两点是否可见 不判断地形)
            isVisible: twoPointConfig,
            // 通视分析（根据地形分析）
            lineOfSight: twoPointConfig,
            // 根据数组计算高程 坡度坡向
            measureZValueByArr: {
                defalutData: {
                    type: 'degree',
                    zFactor: 0.00001,
                },
                request(points, config = {}) {
                    return {
                        arr: points.map((p: any) => ({ x: p[0], y: p[1] })),
                        ...config,
                    }
                },
            },
            // 属性查询
            query2GeoJson: {},
            // 可视域分析
            viewShed: {
                request(data) {
                    const {
                        point,
                        startAngle,
                        viewAngle,
                        viewRadius,
                        width = 300,
                        height = 300,
                    } = data

                    return {
                        imgHeigth: height,
                        imgWidth: width,
                        pointX: point[0] || point.x,
                        pointY: point[1] || point.y,
                        pointZ: point[2] || point.z || 0,
                        startAngle: startAngle,
                        viewAngle: viewAngle,
                        viewRadius: viewRadius,
                    }
                },
                response(res) {
                    res.image = 'data:image/jpeg;base64,' + res.base64code
                    return res
                },
            },
        },
        RequestMethod
    )

    // 路径分析服务
    const navigationAnalyst = getAnalysisService(
        'navigationAnalyst',
        {
            // 路径导航分析
            navigationNetWork: navConfig2,
            // 最少耗费路径分析
            costPathLine: navConfig,
            // 最短表面距离路径分析
            surfacePathLine: {
                defalutData: {
                    wkt: '',
                    maxUpslopeDegree: '80',
                    maxDownslopeDegree: '80',
                },
                ...navConfig,
            },
        },
        RequestMethod
    )

    return {
        ...caculate,
        ...navigationAnalyst,
    }
}
